package com.sigmatrix.h5.config;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.reflection.MetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.parser.ISqlParser;
import com.baomidou.mybatisplus.plugins.parser.SqlInfo;
import com.baomidou.mybatisplus.toolkit.StringUtils;
import com.sigmatrix.h5.entity.base.TablePrefix;

public class TableNameJsqlParser implements ISqlParser, ApplicationListener<ContextRefreshedEvent> {
	private static final Logger LOGGER = LoggerFactory.getLogger(TableNameJsqlParser.class);
	private ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>();
	private Set<String> cacheTablename = new HashSet<>();
	private static String[] specialStr= new String[]{"&&&&","%%%%","@@@@",">>>>","####","{{{{","}}}}"};

	@Autowired
	private ClassScanner scanner;

	@Value("${mybatis-plus.typeAliasesPackage}")
	private String packages;

	@Value("${mybatis-plus.tableName-prefix-exclude:null}")
	private String excludeTableName;

	@Override
	public SqlInfo optimizeSql(MetaObject metaObject, String sql) {
		final List<String[]> placeHolders = new ArrayList<>();
		String tablePrefix = "";
		Object originalObject = metaObject.getOriginalObject();
		if (originalObject != null && originalObject instanceof RoutingStatementHandler) {
			RoutingStatementHandler handler = (RoutingStatementHandler) originalObject;
			BoundSql boundSql = handler.getBoundSql();
			Object parameterObject = boundSql.getParameterObject();
			if (parameterObject != null) {
				if (parameterObject instanceof Map) {
					@SuppressWarnings("unchecked")
					Map<String, Object> map = (Map<String, Object>) parameterObject;
					if (map.containsKey("tablePrefix")) {
						Object tablePrefixObj = map.get("tablePrefix");
						tablePrefix = tablePrefixObj == null ? "" : tablePrefixObj.toString();
					}
					if (map.containsKey("et") && StringUtils.isEmpty(tablePrefix)) {
						Object tablePrefixObj = map.get("et");
						if (tablePrefixObj instanceof TablePrefix) {
							TablePrefix prefix = (TablePrefix) tablePrefixObj;
							tablePrefix = prefix.getTablePrefix();
						}
					}
					if (map.containsKey("ew") && StringUtils.isEmpty(tablePrefix)) {
						Object tablePrefixObj = map.get("ew");
						if (tablePrefixObj instanceof Wrapper) {
							Wrapper<?> wrapper = (Wrapper<?>) tablePrefixObj;
							Object entity = wrapper.getEntity();
							if (entity instanceof TablePrefix) {
								TablePrefix prefix = (TablePrefix) entity;
								tablePrefix = prefix.getTablePrefix();
							}
						}
					}
				} else if (parameterObject instanceof TablePrefix) {
					TablePrefix prefix = (TablePrefix) parameterObject;
					tablePrefix = prefix.getTablePrefix();
				}
			}
		}
		LOGGER.info("Table-Prefix: {}",tablePrefix);
		String sqlUpper = sql.toUpperCase();
		String a = "";
		if(!StringUtils.isEmpty(tablePrefix)){	
			a = cache.get(tablePrefix+sql);
		}else{
			a = cache.get(sql);
		}
		if (a != null) {
			sqlUpper = a;
		} else {
			String _sql = sqlUpper;
			if(!StringUtils.isEmpty(tablePrefix)){		
				for (String _tableName : cacheTablename) {
					if(_sql.contains(_tableName)){
						int size = placeHolders.size();
						String spc = specialStr[size];
//						_sql = _sql.replaceAll(_tableName, spc);
						//站位符替换
						_sql = replaceAll(_sql, _tableName, spc);
						placeHolders.add(new String[]{tablePrefix+_tableName,spc});
					}
				}
				for (String[] placeHolder : placeHolders) {
					//替换真实的表名。
					_sql = _sql.replaceAll(placeHolder[1], placeHolder[0]);
				}
				cache.put(tablePrefix+sql, _sql);
			}else{
				cache.put(sql, _sql);
			}
			sqlUpper = _sql;
		}
		LOGGER.info("Execute SQL: {}",sqlUpper);
		return SqlInfo.newInstance().setSql(sqlUpper);
	}

	@Override
	public void onApplicationEvent(ContextRefreshedEvent arg0) {
		Set<Class<?>> scan = scanner.scan(packages.split(","), TableName.class);
		for (Class<?> class1 : scan) {
			TableName table = class1.getAnnotation(TableName.class);
			if (table != null && StringUtils.isNotEmpty(table.value())) {
				cacheTablename.add(table.value().toUpperCase());
			}
		}

		String[] split = excludeTableName.split(",");
		for (String string : split) {
			cacheTablename.remove(string.toUpperCase());
		}
		LOGGER.info("tableName-prefix-exclude: {}",excludeTableName);
		LOGGER.info("tableName-prefix-include: {}",cacheTablename.toString());

	}
	
	/**
	 * sql 表名前缀替换。
	 * 
	 * @param base
	 * @param tableName
	 * @param replacement
	 * @return
	 */
	private String replaceAll(String base ,String tableName,String replacement){
		if(StringUtils.isEmpty(base)||StringUtils.isEmpty(tableName)||StringUtils.isEmpty(replacement)){
			return base;
		}
		String[] split = base.split(tableName);
		String newSql = "";
		if(split.length==1){
			if(base.contains(tableName)){
				return split[0]+replacement;
			}else{
				return split[0];	
			}
		}
		for (int i = split.length-1; i > 0; i--) {
			if(split[i].length()>0&&Character.isWhitespace(split[i].charAt(0))){
				//是否要优化？
				newSql = replacement+split[i]+newSql;
			}else{
				newSql = tableName+split[i]+newSql;
			}
		}
		newSql = split[0]+newSql;
		return newSql;
		
	}

}
